#include <core/types.h>
#include <flash/flash.h>
#include <flash/nand.h>
#include <flash/nand_ecc.h>
#include <core/bitops.h>
#include <core/errno.h>
#include <malloc.h>
#include <stdio.h>


static int CheckPattern(BYTE *buf, int len, int paglen, struct NandBbtpDesc *td)
{
	int i, end = 0;
	BYTE *p = buf;

	end = paglen + td->offs;
	if (td->dwFlags & NAND_BBT_SCANEMPTY) {
		for (i = 0; i < end; i++) {
			if (p[i] != 0xff)
				return -1;
		}
	}
	p += end;

	
	for (i = 0; i < td->len; i++) {
		if (p[i] != td->pattern[i])
			return -1;
	}

	if (td->dwFlags & NAND_BBT_SCANEMPTY) {
		p += td->len;
		end += td->len;
		for (i = end; i < len; i++) {
			if (*p++ != 0xff)
				return -1;
		}
	}
	return 0;
}


static int CheckShortPattern(BYTE *buf, struct NandBbtpDesc *td)
{
	int i;
	BYTE *p = buf;

	
	for (i = 0; i < td->len; i++)
	{
		if (p[td->offs + i] != td->pattern[i])
			return -1;
	}
	return 0;
}


static int ReadBadBlockTable(struct NandFlash *pNandFlash,
				BYTE *buf,
				int page,
				int num,
				int bits,
				int offs,
				int reserved_block_code
				)
{
	int ret, i, j, act = 0;
	struct Flash *pFlash = NANDFLASH_GET_BASE(pNandFlash);
	ULONG retlen, len, totlen;
	ULONG from;
	BYTE msk = (BYTE) ((1 << bits) - 1);

	totlen = (num * bits) >> 3;
	from = ((ULONG) page) << pFlash->nWriteShift;


	while (totlen)
	{
		len = min(totlen, (ULONG) (1 << pNandFlash->bbt_erase_shift));
		ret = pFlash->Read(pFlash, from, len, &retlen, buf);

		if (ret < 0)
		{
			if (retlen != len)
			{
				printf("%s(): Error reading bad block table\n", __FUNCTION__);
				return ret;
			}
			printf("%s(): ECC error while reading bad block table\n", __FUNCTION__);
		}

		
		for (i = 0; i < len; i++)
		{
			BYTE dat = buf[i];

			for (j = 0; j < 8; j += bits, act += 2)
			{
				BYTE tmp = (dat >> j) & msk;

				if (tmp == msk)
					continue;
				if (reserved_block_code && (tmp == reserved_block_code))
				{
					printf("%s(): Reserved block at 0x%08x\n",
						__FUNCTION__,
						((offs << 2) + (act >> 1)) << pNandFlash->bbt_erase_shift
						);
					pNandFlash->bbt[offs + (act >> 3)] |= 0x2 << (act & 0x06);
					pFlash->mEccStats.bbtblocks++;
					continue;
				}

				printf("%s(): Bad block at 0x%08x\n",
					__FUNCTION__,
					((offs << 2) + (act >> 1)) << pNandFlash->bbt_erase_shift
					);
				
				if (tmp == 0)
					pNandFlash->bbt[offs + (act >> 3)] |= 0x3 << (act & 0x06);
				else
					pNandFlash->bbt[offs + (act >> 3)] |= 0x1 << (act & 0x06);
				pFlash->mEccStats.badblocks++;
			}
		}

		totlen -= len;
		from += len;
	}
	return 0;
}


static int ReadAbsBbt(struct NandFlash *pNandFlash, BYTE *buf, struct NandBbtpDesc *td, int chip)
{
	struct Flash *pFlash = NANDFLASH_GET_BASE(pNandFlash);
	struct NandCtrl *pNandCtrl = pNandFlash->pMaster;
	int ret = 0, i;
	int bits;

	bits = td->dwFlags & NAND_BBT_NRBITS_MSK;

	if (td->dwFlags & NAND_BBT_PERCHIP)
	{
		int offs = 0;

		for (i = 0; i < pNandCtrl->nChips; i++)
		{
			if (chip == -1 || chip == i)
				ret = ReadBadBlockTable(pNandFlash, buf, td->pages[i], pFlash->nChipSize >> pNandFlash->bbt_erase_shift, bits, offs, td->reserved_block_code);

			if (ret)
				return ret;

			offs += pFlash->nChipSize >> (pNandFlash->bbt_erase_shift + 2);
		}
	}
	else
	{
		ret = ReadBadBlockTable(pNandFlash, buf, td->pages[0], pFlash->nChipSize >> pNandFlash->bbt_erase_shift, bits, 0, td->reserved_block_code);

		if (ret)
			return ret;
	}
	return 0;
}


static int ScanReadRaw(struct NandFlash *pNandFlash,
					BYTE *buf,
					ULONG offs,
					ULONG len
					)
{
	struct OobOpt ops;
	struct Flash *pFlash = NANDFLASH_GET_BASE(pNandFlash);

	ops.eOpMode    = FLASH_OOB_RAW;
	ops.dwOpOobOff = 0;
	ops.dwOpOobLen    = pFlash->nOobSize;
	ops.pOpOobBuf  = buf;
	ops.pOpDatBuf  = buf;
	ops.dwOpLen    = len;

	return pFlash->ReadOob(pFlash, offs, &ops);
}


static int ScanWriteBbt(struct NandFlash *pNandFlash,
						ULONG offs,
						ULONG len,
						BYTE *buf,
						BYTE *oob
						)
{
	struct OobOpt ops;
	struct Flash *pFlash = NANDFLASH_GET_BASE(pNandFlash);

	ops.eOpMode = FLASH_OOB_PLACE;
	ops.dwOpOobOff = 0;
	ops.dwOpOobLen = pFlash->nOobSize;
	ops.pOpDatBuf = buf;
	ops.pOpOobBuf = oob;
	ops.dwOpLen = len;

	return pFlash->WriteOob(pFlash, offs, &ops);
}


static int ReadAbsBbts(struct NandFlash *pNandFlash,
						BYTE *buf,
						struct NandBbtpDesc *td,
						struct NandBbtpDesc *md
						)
{
	struct Flash *pFlash = NANDFLASH_GET_BASE(pNandFlash);

	
	if (td->dwFlags & NAND_BBT_VERSION)
	{
		ScanReadRaw(pNandFlash,
				buf,
				td->pages[0] << pFlash->nWriteShift,
				pFlash->nWriteSize
				);
		td->version[0] = buf[pFlash->nWriteSize + td->veroffs];
		printf("Bad block table at page %d, version 0x%02X\n", td->pages[0], td->version[0]);
	}

	if (md && (md->dwFlags & NAND_BBT_VERSION))
	{
		ScanReadRaw(pNandFlash,
				buf,
				md->pages[0] << pFlash->nWriteShift,
				pFlash->nWriteSize
				);
		md->version[0] = buf[pFlash->nWriteSize + md->veroffs];
		printf("Bad block table at page %d, version 0x%02X\n", md->pages[0], md->version[0]);
	}

	return 1;
}


static int ScanBlockFull(struct NandFlash *pNandFlash,
						struct NandBbtpDesc *bd,
						ULONG offs,
						BYTE *buf,
						ULONG readlen,
						int scanlen,
						int len
						)
{
	int ret, j;
	struct Flash *pFlash = NANDFLASH_GET_BASE(pNandFlash);

	ret = ScanReadRaw(pNandFlash, buf, offs, readlen);
	if (ret)
		return ret;

	for (j = 0; j < len; j++, buf += scanlen)
	{
		if (CheckPattern(buf, scanlen, pFlash->nWriteSize, bd))
			return 1;
	}
	return 0;
}


static int ScanBlockFast(struct NandFlash *pNandFlash,
						struct NandBbtpDesc *bd,
						ULONG offs,
						BYTE *buf,
						int len
						)
{
	struct OobOpt ops;
	int j, ret;
	struct Flash *pFlash = NANDFLASH_GET_BASE(pNandFlash);

	ops.dwOpOobLen = pFlash->nOobSize;
	ops.pOpOobBuf = buf;
	ops.dwOpOobOff = 0;
	ops.pOpDatBuf = NULL;
	ops.eOpMode = FLASH_OOB_PLACE;

	for (j = 0; j < len; j++) {
		/*
		 * Read the full oob until read_oob is fixed to
		 * handle single byte reads for 16 bit
		 * buswidth
		 */
		ret = pFlash->ReadOob(pFlash, offs, &ops);
		if (ret)
			return ret;

		if (CheckShortPattern(buf, bd))
			return 1;

		offs += pFlash->nWriteSize;
	}
	return 0;
}


static int CreateBadBlockTable(struct NandFlash *pNandFlash,
					BYTE *buf,
					struct NandBbtpDesc *bd,
					int chip
					)
{
	struct Flash *pFlash = NANDFLASH_GET_BASE(pNandFlash);
	struct NandCtrl *pNandCtrl = pNandFlash->pMaster;
	int i, numblocks, len, scanlen;
	int startblock;
	ULONG from;
	ULONG readlen;


	if (bd->dwFlags & NAND_BBT_SCANALLPAGES)
		len = 1 << (pNandFlash->bbt_erase_shift - pFlash->nWriteShift);
	else {
		if (bd->dwFlags & NAND_BBT_SCAN2NDPAGE)
			len = 2;
		else
			len = 1;
	}

	if (!(bd->dwFlags & NAND_BBT_SCANEMPTY)) {
		
		scanlen = 0;
		readlen = bd->len;
	} else {
		
		scanlen = pFlash->nWriteSize + pFlash->nOobSize;
		readlen = len * pFlash->nWriteSize;
	}

	if (chip == -1) {
		/* Note that numblocks is 2 * (real numblocks) here, see i+=2
		 * below as it makes shifting and masking less painful */
		numblocks = pFlash->nChipSize >> (pNandFlash->bbt_erase_shift - 1);
		startblock = 0;
		from = 0;
	} else {
		if (chip >= pNandCtrl->nChips) {
			printf("CreateBadBlockTable(): chipnr (%d) > available chips (%d)\n",
				chip + 1, pNandCtrl->nChips);
			return -EINVAL;
		}
		numblocks = pFlash->nChipSize >> (pNandFlash->bbt_erase_shift - 1);
		startblock = chip * numblocks;
		numblocks += startblock;
		from = startblock << (pNandFlash->bbt_erase_shift - 1);
	}

	for (i = startblock; i < numblocks; )
	{
		int ret;

		if (bd->dwFlags & NAND_BBT_SCANALLPAGES)
			ret = ScanBlockFull(pNandFlash, bd, from, buf, readlen,
						scanlen, len);
		else
			ret = ScanBlockFast(pNandFlash, bd, from, buf, len);

		if (ret < 0)
			return ret;

		if (ret)
		{
			pNandFlash->bbt[i >> 3] |= 0x03 << (i & 0x6);
			printf("Bad eraseblock %d at 0x%08x\n", i >> 1, from);
			pFlash->mEccStats.badblocks++;
		}

		i += 2;
		from += (1 << pNandFlash->bbt_erase_shift);
	}
	return 0;
}


static int SearchBbt(struct NandFlash *pNandFlash, BYTE *buf, struct NandBbtpDesc *td)
{
	struct Flash *pFlash = NANDFLASH_GET_BASE(pNandFlash);
	struct NandCtrl *pNandCtrl = pNandFlash->pMaster;
	int i, chips;
	int bits, startblock, block, dir;
	int scanlen = pFlash->nWriteSize + pFlash->nOobSize;
	int bbtblocks;
	int blocktopage = pNandFlash->bbt_erase_shift - pFlash->nWriteShift;

	
	if (td->dwFlags & NAND_BBT_LASTBLOCK) {
		startblock = (pFlash->nChipSize >> pNandFlash->bbt_erase_shift) - 1;
		dir = -1;
	} else {
		startblock = 0;
		dir = 1;
	}

	
	if (td->dwFlags & NAND_BBT_PERCHIP) {
		chips = pNandCtrl->nChips;
		bbtblocks = pFlash->nChipSize >> pNandFlash->bbt_erase_shift;
		startblock &= bbtblocks - 1;
	} else {
		chips = 1;
		bbtblocks = pFlash->nChipSize >> pNandFlash->bbt_erase_shift;
	}

	
	bits = td->dwFlags & NAND_BBT_NRBITS_MSK;

	for (i = 0; i < chips; i++) {
		
		td->version[i] = 0;
		td->pages[i] = -1;
		
		for (block = 0; block < td->maxblocks; block++) {

			int actblock = startblock + dir * block;
			ULONG offs = actblock << pNandFlash->bbt_erase_shift;

			ScanReadRaw(pNandFlash, buf, offs, pFlash->nWriteSize);
			if (!CheckPattern(buf, scanlen, pFlash->nWriteSize, td)) {
				td->pages[i] = actblock << blocktopage;
				if (td->dwFlags & NAND_BBT_VERSION) {
					td->version[i] = buf[pFlash->nWriteSize + td->veroffs];
				}
				break;
			}
		}
		startblock += pFlash->nChipSize >> pNandFlash->bbt_erase_shift;
	}
	
	for (i = 0; i < chips; i++) {
		if (td->pages[i] == -1)
			printf("Bad block table not found for chip %d\n", i);
		else
			printf("Bad block table found at page %d, version 0x%02X\n", td->pages[i],
				td->version[i]);
	}
	return 0;
}


static int SearchReadBbts(struct NandFlash *pNandFlash,
							BYTE * buf,
							struct NandBbtpDesc *td,
							struct NandBbtpDesc *md
							)
{
	
	SearchBbt(pNandFlash, buf, td);

	
	if (md)
		SearchBbt(pNandFlash, buf, md);

	
	return 1;
}


static int WriteBadBlockTable(struct NandFlash *pNandFlash,
				BYTE *buf,
				struct NandBbtpDesc *td,
				struct NandBbtpDesc *md,
				int chipsel
				)
{
	struct Flash *pFlash = NANDFLASH_GET_BASE(pNandFlash);
	struct NandCtrl *pNandCtrl = pNandFlash->pMaster;
	struct FerOpt einfo;
	int i, j, ret, chip = 0;
	int bits, startblock, dir, page, offs, numblocks, sft, sftmsk;
	int nrchips, bbtoffs, pageoffs, dwOpOobOff;
	BYTE msk[4];
	BYTE rcode = td->reserved_block_code;
	ULONG retlen, len = 0;
	ULONG to;
	struct OobOpt ops;

	ops.dwOpOobLen = pFlash->nOobSize;
	ops.dwOpOobOff = 0;
	ops.pOpDatBuf = NULL;
	ops.eOpMode = FLASH_OOB_PLACE;

	if (!rcode)
		rcode = 0xff;
	
	if (td->dwFlags & NAND_BBT_PERCHIP) {
		numblocks = (int)(pFlash->nChipSize >> pNandFlash->bbt_erase_shift);
		
		if (chipsel == -1) {
			nrchips = pNandCtrl->nChips;
		} else {
			nrchips = chipsel + 1;
			chip = chipsel;
		}
	} else {
		numblocks = (int)(pFlash->nChipSize >> pNandFlash->bbt_erase_shift);
		nrchips = 1;
	}

	
	for (; chip < nrchips; chip++) {

		/* There was already a version of the table, reuse the page
		 * This applies for absolute placement too, as we have the
		 * page nr. in td->pages.
		 */
		if (td->pages[chip] != -1) {
			page = td->pages[chip];
			goto write;
		}

		
		
		if (td->dwFlags & NAND_BBT_LASTBLOCK) {
			startblock = numblocks * (chip + 1) - 1;
			dir = -1;
		} else {
			startblock = chip * numblocks;
			dir = 1;
		}

		for (i = 0; i < td->maxblocks; i++) {
			int block = startblock + dir * i;
			
			switch ((pNandFlash->bbt[block >> 2] >>
				 (2 * (block & 0x03))) & 0x03) {
			case 0x01:
			case 0x03:
				continue;
			}
			page = block << (pNandFlash->bbt_erase_shift - pFlash->nWriteShift);
			
			if (!md || md->pages[chip] != page)
				goto write;
		}
		printf("No space left to write bad block table\n");
		return -ENOSPC;

write:
		bits = td->dwFlags & NAND_BBT_NRBITS_MSK;
		msk[2] = ~rcode;
		switch (bits) {
		case 1: sft = 3; sftmsk = 0x07; msk[0] = 0x00; msk[1] = 0x01;
			msk[3] = 0x01;
			break;
		case 2: sft = 2; sftmsk = 0x06; msk[0] = 0x00; msk[1] = 0x01;
			msk[3] = 0x03;
			break;
		case 4: sft = 1; sftmsk = 0x04; msk[0] = 0x00; msk[1] = 0x0C;
			msk[3] = 0x0f;
			break;
		case 8: sft = 0; sftmsk = 0x00; msk[0] = 0x00; msk[1] = 0x0F;
			msk[3] = 0xff;
			break;
		default:
			return -EINVAL;
		}

		bbtoffs = chip * (numblocks >> 2);

		to = ((ULONG) page) << pFlash->nWriteShift;

		
		if (td->dwFlags & NAND_BBT_SAVECONTENT)
		{
			to &= ~((ULONG) ((1 << pNandFlash->bbt_erase_shift) - 1));
			len = 1 << pNandFlash->bbt_erase_shift;
			ret = pFlash->Read(pFlash, to, len, &retlen, buf);
			if (ret < 0) {
				if (retlen != len) {
					printf("nand_bbt: Error "
						"reading block for writing "
						"the bad block table\n");
					return ret;
				}
				printf("nand_bbt: ECC error "
					"while reading block for writing "
					"bad block table\n");
			}
			
			ops.dwOpOobLen = (len >> pFlash->nWriteShift) * pFlash->nOobSize;
			ops.pOpOobBuf = &buf[len];
			ret = pFlash->ReadOob(pFlash, to + pFlash->nWriteSize, &ops);
			if (ret < 0 || ops.dwOpOobRetLen != ops.dwOpOobLen)
				goto outerr;

			
			pageoffs = page - (int)(to >> pFlash->nWriteShift);
			offs = pageoffs << pFlash->nWriteShift;
			
			memset(&buf[offs], 0xff, (ULONG) (numblocks >> sft));
			dwOpOobOff = len + (pageoffs * pFlash->nOobSize);

		} else {
			
			len = (ULONG) (numblocks >> sft);
			
			len = (len + (pFlash->nWriteSize - 1)) &
				~(pFlash->nWriteSize - 1);
			
			memset(buf, 0xff, len +
				(len >> pFlash->nWriteShift)* pFlash->nOobSize);
			offs = 0;
			dwOpOobOff = len;
			
			memcpy(&buf[dwOpOobOff + td->offs], td->pattern, td->len);
		}

		if (td->dwFlags & NAND_BBT_VERSION)
			buf[dwOpOobOff + td->veroffs] = td->version[chip];

		
		for (i = 0; i < numblocks;) {
			BYTE dat;
			dat = pNandFlash->bbt[bbtoffs + (i >> 2)];
			for (j = 0; j < 4; j++, i++) {
				int sftcnt = (i << (3 - sft)) & sftmsk;
				
				buf[offs + (i >> sft)] &=
					~(msk[dat & 0x03] << sftcnt);
				dat >>= 2;
			}
		}

		pFlash->isBadAllow = 1;
		memset(&einfo, 0, sizeof(einfo));
		einfo.pFlash      = pFlash;
		einfo.dwEraseAddr = (unsigned long)to;
		einfo.dwEraseLen  = 1 << pNandFlash->bbt_erase_shift;
		ret = NandErase(pNandFlash, &einfo);
		if (ret < 0)
			goto outerr;

		ret = ScanWriteBbt(pNandFlash, to, len, buf, &buf[len]);
		if (ret < 0)
			goto outerr;

		printf("Bad block table written to 0x%08x, version "
			"0x%02X\n", (unsigned int)to, td->version[chip]);

		
		td->pages[chip] = page;
	}
	return 0;

 outerr:
	printf(
		"nand_bbt: Error while writing bad block table %d\n", ret);
	return ret;
}


static inline int NandMemoryBbt(struct NandFlash *pNandFlash, struct NandBbtpDesc *bd)
{
	struct Flash *pFlash = NANDFLASH_GET_BASE(pNandFlash);

	bd->dwFlags &= ~NAND_BBT_SCANEMPTY;
	return CreateBadBlockTable(pNandFlash, pNandFlash->buffers->pDataBuf, bd, -1);
}


static int CheckCreate(struct NandFlash *pNandFlash, BYTE *buf, struct NandBbtpDesc *bd)
{
	int i, chips, writeops, chipsel, ret;

	struct Flash *pFlash = NANDFLASH_GET_BASE(pNandFlash);
	struct NandCtrl *pNandCtrl = pNandFlash->pMaster;

	struct NandBbtpDesc *td = pNandFlash->bbt_td;
	struct NandBbtpDesc *md = pNandFlash->bbt_md;
	struct NandBbtpDesc *rd, *rd2;

	
	if (td->dwFlags & NAND_BBT_PERCHIP)
		chips = pNandCtrl->nChips;
	else
		chips = 1;

	for (i = 0; i < chips; i++) {
		writeops = 0;
		rd = NULL;
		rd2 = NULL;
		
		chipsel = (td->dwFlags & NAND_BBT_PERCHIP) ? i : -1;
		
		if (md) {
			if (td->pages[i] == -1 && md->pages[i] == -1) {
				writeops = 0x03;
				goto create;
			}

			if (td->pages[i] == -1) {
				rd = md;
				td->version[i] = md->version[i];
				writeops = 1;
				goto writecheck;
			}

			if (md->pages[i] == -1) {
				rd = td;
				md->version[i] = td->version[i];
				writeops = 2;
				goto writecheck;
			}

			if (td->version[i] == md->version[i]) {
				rd = td;
				if (!(td->dwFlags & NAND_BBT_VERSION))
					rd2 = md;
				goto writecheck;
			}

			if (((char)(td->version[i] - md->version[i])) > 0) {
				rd = td;
				md->version[i] = td->version[i];
				writeops = 2;
			} else {
				rd = md;
				td->version[i] = md->version[i];
				writeops = 1;
			}

			goto writecheck;

		} else {
			if (td->pages[i] == -1) {
				writeops = 0x01;
				goto create;
			}
			rd = td;
			goto writecheck;
		}
	create:
		
		if (!(td->dwFlags & NAND_BBT_CREATE))
			continue;

		
		CreateBadBlockTable(pNandFlash, buf, bd, chipsel);

		td->version[i] = 1;
		if (md)
			md->version[i] = 1;
	writecheck:
		
		if (rd)
			ReadAbsBbt(pNandFlash, buf, rd, chipsel);
		
		if (rd2)
			ReadAbsBbt(pNandFlash, buf, rd2, chipsel);

		
		if ((writeops & 0x01) && (td->dwFlags & NAND_BBT_WRITE)) {
			ret = WriteBadBlockTable(pNandFlash, buf, td, md, chipsel);
			if (ret < 0)
				return ret;
		}

		
		if ((writeops & 0x02) && md && (md->dwFlags & NAND_BBT_WRITE)) {
			ret = WriteBadBlockTable(pNandFlash, buf, md, td, chipsel);
			if (ret < 0)
				return ret;
		}
	}
	return 0;
}


static void MarkBbtRegion(struct NandFlash *pNandFlash, struct NandBbtpDesc *td)
{
	struct Flash *pFlash = NANDFLASH_GET_BASE(pNandFlash);
	struct NandCtrl *pNandCtrl = pNandFlash->pMaster;

	int i, j, chips, block, nrblocks, update;
	BYTE oldval, newval;

	
	if (td->dwFlags & NAND_BBT_PERCHIP) {
		chips = pNandCtrl->nChips;
		nrblocks = (int)(pFlash->nChipSize >> pNandFlash->bbt_erase_shift);
	} else {
		chips = 1;
		nrblocks = (int)(pFlash->nChipSize >> pNandFlash->bbt_erase_shift);
	}

	for (i = 0; i < chips; i++) {
		if ((td->dwFlags & NAND_BBT_ABSPAGE) ||
			!(td->dwFlags & NAND_BBT_WRITE)) {
			if (td->pages[i] == -1)
				continue;
			block = td->pages[i] >> (pNandFlash->bbt_erase_shift - pFlash->nWriteShift);
			block <<= 1;
			oldval = pNandFlash->bbt[(block >> 3)];
			newval = oldval | (0x2 << (block & 0x06));
			pNandFlash->bbt[(block >> 3)] = newval;
			if ((oldval != newval) && td->reserved_block_code)
				NandUpdateBbt(pNandFlash, block << (pNandFlash->bbt_erase_shift - 1));
			continue;
		}
		update = 0;
		if (td->dwFlags & NAND_BBT_LASTBLOCK)
			block = ((i + 1) * nrblocks) - td->maxblocks;
		else
			block = i * nrblocks;
		block <<= 1;
		for (j = 0; j < td->maxblocks; j++) {
			oldval = pNandFlash->bbt[(block >> 3)];
			newval = oldval | (0x2 << (block & 0x06));
			pNandFlash->bbt[(block >> 3)] = newval;
			if (oldval != newval)
				update = 1;
			block += 2;
		}
		/* If we want reserved blocks to be recorded to flash, and some
		   new ones have been marked, then we need to update the stored
		   bbts.  This should only happen once. */
		if (update && td->reserved_block_code)
			NandUpdateBbt(pNandFlash, (block - 2) << (pNandFlash->bbt_erase_shift - 1));
	}
}


int NandScanBadBlock(struct NandFlash *pNandFlash, struct NandBbtpDesc *bd)
{
	struct Flash *pFlash = NANDFLASH_GET_BASE(pNandFlash);
	int len, ret = 0;
	BYTE *buf;
	struct NandBbtpDesc *td = pNandFlash->bbt_td;
	struct NandBbtpDesc *md = pNandFlash->bbt_md;

	len = pFlash->nChipSize >> (pNandFlash->bbt_erase_shift + 2);

	
	pNandFlash->bbt = kzalloc(len, GFP_KERNEL);
	if (!pNandFlash->bbt) 
	{
		printf("%s(): Out of memory\n", __FUNCTION__);
		return -ENOMEM;
	}

	/* If no primary table decriptor is given, scan the device
	 * to build a memory based bad block table
	 */
	if (!td) {
		if ((ret = NandMemoryBbt(pNandFlash, bd))) {
			printf("nand_bbt: Can't scan flash and build the RAM-based BBT\n");
			kfree(pNandFlash->bbt);
			pNandFlash->bbt = NULL;
		}
		return ret;
	}

	
	len = (1 << pNandFlash->bbt_erase_shift);
	len += (len >> pFlash->nWriteShift) * pFlash->nOobSize;
	buf =  kmalloc(len, GFP_KERNEL);
	if (!buf) {
		printf("nand_bbt: Out of memory\n");
		kfree(pNandFlash->bbt);
		pNandFlash->bbt = NULL;
		return -ENOMEM;
	}

	
	if (td->dwFlags & NAND_BBT_ABSPAGE) {
		ret = ReadAbsBbts(pNandFlash, buf, td, md);
	} else {
		
		ret = SearchReadBbts(pNandFlash, buf, td, md);
	}

	if (ret)
		ret = CheckCreate(pNandFlash, buf, bd);

	
	MarkBbtRegion(pNandFlash, td);
	if (md)
		MarkBbtRegion(pNandFlash, md);

	kfree(buf);
	return ret;
}


int NandUpdateBbt(struct NandFlash *pNandFlash, ULONG offs)
{
	struct Flash *pFlash = NANDFLASH_GET_BASE(pNandFlash);
	int len, ret = 0, writeops = 0;
	int chip, chipsel;
	BYTE *buf;
	struct NandBbtpDesc *td = pNandFlash->bbt_td;
	struct NandBbtpDesc *md = pNandFlash->bbt_md;

	if (!pNandFlash->bbt || !td)
		return -EINVAL;

	len = pFlash->nChipSize >> (pNandFlash->bbt_erase_shift + 2);
	
	len = (1 << pNandFlash->bbt_erase_shift);
	len += (len >> pFlash->nWriteShift) * pFlash->nOobSize;
	buf = kmalloc(len, GFP_KERNEL);
	if (!buf) {
		printf("NandUpdateBbt: Out of memory\n");
		return -ENOMEM;
	}

	writeops = md != NULL ? 0x03 : 0x01;

	
	if (td->dwFlags & NAND_BBT_PERCHIP) {
		chip = (int)(offs >> pFlash->nChipShift);
		chipsel = chip;
	} else {
		chip = 0;
		chipsel = -1;
	}

	td->version[chip]++;
	if (md)
		md->version[chip]++;

	
	if ((writeops & 0x01) && (td->dwFlags & NAND_BBT_WRITE)) {
		ret = WriteBadBlockTable(pNandFlash, buf, td, md, chipsel);
		if (ret < 0)
			goto out;
	}
	
	if ((writeops & 0x02) && md && (md->dwFlags & NAND_BBT_WRITE)) {
		ret = WriteBadBlockTable(pNandFlash, buf, md, td, chipsel);
	}

 out:
	kfree(buf);
	return ret;
}


static BYTE scan_ff_pattern[] = {0xff, 0xff};

static struct NandBbtpDesc smallpage_memorybased = {
	.dwFlags = NAND_BBT_SCAN2NDPAGE,
	.offs = 5,
	.len = 1,
	.pattern = scan_ff_pattern
};

static struct NandBbtpDesc largepage_memorybased = {
	.dwFlags = 0,
	.offs = 0,
	.len = 2,
	.pattern = scan_ff_pattern
};

static struct NandBbtpDesc smallpage_flashbased = {
	.dwFlags = NAND_BBT_SCAN2NDPAGE,
	.offs = 5,
	.len = 1,
	.pattern = scan_ff_pattern
};

static struct NandBbtpDesc largepage_flashbased = {
	.dwFlags = NAND_BBT_SCAN2NDPAGE,
	.offs = 0,
	.len = 2,
	.pattern = scan_ff_pattern
};

static BYTE scan_agand_pattern[] = { 0x1C, 0x71, 0xC7, 0x1C, 0x71, 0xC7 };

static struct NandBbtpDesc agand_flashbased = {
	.dwFlags = NAND_BBT_SCANEMPTY | NAND_BBT_SCANALLPAGES,
	.offs = 0x20,
	.len = 6,
	.pattern = scan_agand_pattern
};


static BYTE bbt_pattern[] = {'B', 'b', 't', '0' };
static BYTE mirror_pattern[] = {'1', 't', 'b', 'B' };

static struct NandBbtpDesc g_bbtMainpDesc = {
	.dwFlags = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
		| NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
	.offs =	8,
	.len = 4,
	.veroffs = 12,
	.maxblocks = 4,
	.pattern = bbt_pattern
};

static struct NandBbtpDesc g_bbtMirrorpDesc = {
	.dwFlags = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
		| NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
	.offs =	8,
	.len = 4,
	.veroffs = 12,
	.maxblocks = 4,
	.pattern = mirror_pattern
};


int NandScanBbt(struct NandFlash *pNandFlash)
{
	struct Flash *pFlash = NANDFLASH_GET_BASE(pNandFlash);


	if (pNandFlash->dwFlags & NAND_IS_AND)
	{
		if (!pNandFlash->bbt_td)
		{
			pNandFlash->bbt_td = &g_bbtMainpDesc;
			pNandFlash->bbt_md = &g_bbtMirrorpDesc;
		}

		pNandFlash->dwFlags |= NAND_USE_FLASH_BBT;

		return NandScanBadBlock(pNandFlash, &agand_flashbased);
	}

	
	if (pNandFlash->dwFlags & NAND_USE_FLASH_BBT)
	{
		if (!pNandFlash->bbt_td)
		{
			pNandFlash->bbt_td = &g_bbtMainpDesc;
			pNandFlash->bbt_md = &g_bbtMirrorpDesc;
		}

		if (!pNandFlash->badblock_pattern)
		{
			pNandFlash->badblock_pattern = (pFlash->nWriteSize > 512) ? &largepage_flashbased : &smallpage_flashbased;
		}
	}
	else
	{
		pNandFlash->bbt_td = NULL;
		pNandFlash->bbt_md = NULL;
		if (!pNandFlash->badblock_pattern)
		{
			pNandFlash->badblock_pattern = (pFlash->nWriteSize > 512) ?
				&largepage_memorybased : &smallpage_memorybased;
		}
	}

	return NandScanBadBlock(pNandFlash, pNandFlash->badblock_pattern);
}


int NandIsBadBbt(struct NandFlash *pNandFlash, ULONG offs)
{
	struct Flash *pFlash = NANDFLASH_GET_BASE(pNandFlash);
	int block;
	BYTE ret;

	
	block = (int)(offs >> (pNandFlash->bbt_erase_shift - 1));
	ret = (pNandFlash->bbt[block >> 3] >> (block & 0x06)) & 0x03;

	DPRINT("%s(): bbt info for offs 0x%08x: (block %d) 0x%02x\n",
		__FUNCTION__,
		offs,
		block >> 1,
		ret
		);

	switch (ret)
	{
	case 0x00:
	case 0x01:
		return ret;
	case 0x02:
		return pFlash->isBadAllow ? 0 : 1;
	}

	return 1;
}

 
